package com.spring.sd;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import javax.annotation.PostConstruct;
import java.util.Arrays;

/**
 * 如果要将 Bean 添加到 Spring 容器中，需要先根据配置文件或注解信息为每一个 Bean 生成一个 BeanDefinition，
 * 然后将这些 BeanDefinition 添加到 BeanDefinitionRegistry 中，当创建 Bean 对象时，直接从 BeanDefinitionRegistry 中获取 BeanDefinition 来生成 Bean。
 *
 * 如果生成的 Bean 是单例的，Spring 会将它们保存到SingletonBeanRegistry 中，后续需要时从这里面寻找，避免重复创建。
 *
 * 那么向 Spring 容器中添加单例 Bean 时，可以跳过注册 BeanDefinition，直接向 SingletonBeanRegistry 中添加创建完成的 Bean。
 * 既然添加的是创建完成的 Bean，所以 这个 Bean 不会经过 Spring 的生命周期。
 *
 * SingletonBeanRegistry 是一个接口，它有一个子接口名为 ConfigurableListableBeanFactory，
 * 而这恰好是 BeanFactoryPostProcessor 接口中抽象方法的参数：
 *     public interface BeanFactoryPostProcessor {
 *         void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
 *     }
 *
 */
public class RegistryFinishedBean {


    public static void main(String[] args) {
        // BeanDefinition 的名称数组中不包含 bean1，也没有输出任何与经过 Spring 生命周期相关的日志信息，
        // 容器中 bean1 里注入的 bean2 也是 null。这表明通过这种方式注册的 Bean 不会注册 BeanDefinition，也不会经过 Spring 生命周期。

        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.registerBean("bean2", Bean2.class);
        context.registerBean(MyBeanFactoryPostProcessor.class);
        context.refresh();

        Arrays.stream(context.getBeanDefinitionNames()).forEach(dfName -> System.out.println(">>>>>>>>>>> " + dfName));
        System.out.println("------------------------------------");
        System.out.println(context.getBean(Bean1.class));
    }

    static class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
        @Override
        public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
            Bean1 bean1 = new Bean1();
            bean1.setName("mofan");
            beanFactory.registerSingleton("bean1", bean1);
        }
    }

    @Getter
    @ToString
    @Slf4j
    static class Bean1 {
        @Setter
        private String name;
        private Bean2 bean2;

        @Autowired
        private void setBean2(Bean2 bean2) {
            log.debug("依赖注入 bean2");
            this.bean2 = bean2;
        }

        @PostConstruct
        public void init() {
            log.debug("初始化...");
        }
    }

    static class Bean2 {
    }
}
